/* RxRPC test server
 *
 * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
 * Written by David Howells (dhowells@redhat.com)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 */

#define _XOPEN_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <ctype.h>
#include <time.h>
#include <signal.h>
#include <errno.h>
#include <termios.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <keyutils.h>

// drive with:
// vos listvldb -encrypt

// or:
#define DO_BULK
// openafs-1.4.2/src/rx/bulk.example/bulk_client -store /usr/share/doc/emacs-common-21.4/NEWS 172.16.18.111 /tmp/a 

#define OSERROR(X, Y) do { if ((long)(X) == -1) { perror(Y); exit(1); } } while(0)

struct sockaddr_rxrpc {
	sa_family_t	srx_family;	/* address family */
	unsigned short	srx_service;	/* service desired */
	unsigned short	transport_type;	/* type of transport socket (SOCK_DGRAM) */
	unsigned short	transport_len;	/* length of transport address */
	union {
		sa_family_t family;		/* transport address family */
		struct sockaddr_in sin;		/* IPv4 transport address */
		struct sockaddr_in6 sin6;	/* IPv6 transport address */
	} transport;
};

#define AF_RXRPC		33
#define PF_RXRPC		AF_RXRPC
#define SOL_RXRPC		272
#define RXRPC_USER_CALL_ID	1	/* User call ID specifier */
#define RXRPC_ABORT		2	/* Abort request / notification */
#define RXRPC_ACK		3	/* [Server] RPC op final ACK received */
#define RXRPC_RESPONSE		4	/* [Server] security response received */
#define RXRPC_NET_ERROR		5	/* network error received */
#define RXRPC_BUSY		6	/* server busy received */
#define RXRPC_LOCAL_ERROR	7	/* local error generated */
#define RXRPC_NEW_CALL		8	/* [Server] new incoming call notification */
#define RXRPC_ACCEPT		9	/* [Server] accept request */
#define RXRPC_SECURITY_KEY		1	/* [clnt] set client security key */
#define RXRPC_SECURITY_KEYRING		2	/* [srvr] set ring of server security keys */
#define RXRPC_EXCLUSIVE_CONNECTION	3	/* [clnt] use exclusive RxRPC connection */
#define RXRPC_MIN_SECURITY_LEVEL	4	/* minimum security level */

#define OSERROR(X, Y) do { if ((long)(X) == -1) { perror(Y); exit(1); } } while(0)

static const unsigned char local_addr[4] = { 0, 0, 0, 0 };

#define RXRPC_ADD_CALLID(control, ctrllen, id)				\
do {									\
	struct cmsghdr *__cmsg;						\
	__cmsg = (void *)(control) + (ctrllen);				\
	__cmsg->cmsg_len	= CMSG_LEN(sizeof(unsigned long));	\
	__cmsg->cmsg_level	= SOL_RXRPC;				\
	__cmsg->cmsg_type	= RXRPC_USER_CALL_ID;			\
	*(unsigned long *)CMSG_DATA(__cmsg) = (id);			\
	(ctrllen) += __cmsg->cmsg_len;					\
} while (0)

#define RXRPC_ADD_ABORT(control, ctrllen, abort_code)			\
do {									\
	struct cmsghdr *__cmsg;						\
	__cmsg = (void *)(control) + (ctrllen);				\
	__cmsg->cmsg_len	= CMSG_LEN(sizeof(unsigned long));	\
	__cmsg->cmsg_level	= SOL_RXRPC;				\
	__cmsg->cmsg_type	= RXRPC_ABORT;				\
	*(unsigned long *)CMSG_DATA(__cmsg) = (abort_code);		\
	(ctrllen) += __cmsg->cmsg_len;					\
} while (0)

#define RXRPC_ADD_ACCEPT(control, ctrllen)				\
do {									\
	struct cmsghdr *__cmsg;						\
	__cmsg = (void *)(control) + (ctrllen);				\
	__cmsg->cmsg_len	= CMSG_LEN(0);				\
	__cmsg->cmsg_level	= SOL_RXRPC;				\
	__cmsg->cmsg_type	= RXRPC_ACCEPT;				\
	(ctrllen) += __cmsg->cmsg_len;					\
} while (0)

int server;

/*****************************************************************************/
/*
 * send a reply to an operation
 */
static void send_reply(unsigned long user_id)
{
	unsigned char control[4096];
	struct msghdr msg;
	size_t ctrllen;
	int ret;

	ctrllen = 0;

	RXRPC_ADD_CALLID(control, ctrllen, user_id);

	msg.msg_name		= NULL;
	msg.msg_namelen		= 0;
	msg.msg_iov		= NULL;
	msg.msg_iovlen		= 0;
	msg.msg_control		= control;
	msg.msg_controllen	= ctrllen;
	msg.msg_flags		= 0;

	ret = sendmsg(server, &msg, 0);
	OSERROR(ret, "sendmsg/reply");
}

/*****************************************************************************/
/*
 * assign a call ID to a new incoming call
 */
static void assign_call_ID(void)
{
	unsigned char control[4096];
	struct msghdr msg;
	size_t ctrllen;
	int ret;

	static unsigned long call_ID = 0x10101;

	ctrllen = 0;

	RXRPC_ADD_ACCEPT(control, ctrllen);
	RXRPC_ADD_CALLID(control, ctrllen, call_ID++);

	msg.msg_name		= NULL;
	msg.msg_namelen		= 0;
	msg.msg_iov		= NULL;
	msg.msg_iovlen		= 0;
	msg.msg_control		= control;
	msg.msg_controllen	= ctrllen;
	msg.msg_flags		= 0;

	ret = sendmsg(server, &msg, 0);
	OSERROR(ret, "sendmsg/accept");
}

/*****************************************************************************/
/*
 * dump the control messages
 */
static void dump_cmsg(struct msghdr *msg)
{
	struct cmsghdr *cmsg;
	unsigned long user_id;
	unsigned char *p;
	int abort_code;
	int n, assign = 0, nondata = 0;

	for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
		n = cmsg->cmsg_len - CMSG_ALIGN(sizeof(*cmsg));
		p = CMSG_DATA(cmsg);

		printf("CMSG: %zu: ", cmsg->cmsg_len);

		if (cmsg->cmsg_level == SOL_RXRPC) {
			switch (cmsg->cmsg_type) {
			case RXRPC_USER_CALL_ID:
				printf("RXRPC_USER_CALL_ID: ");
				if (n != sizeof(user_id))
					goto dump_data;
				memcpy(&user_id, p, sizeof(user_id));
				printf("%lx\n", user_id);
				continue;

			case RXRPC_ABORT:
				nondata = 1;
				printf("RXRPC_ABORT: ");
				if (n != sizeof(abort_code))
					goto dump_data;
				memcpy(&abort_code, p, sizeof(abort_code));
				printf("%d\n", abort_code);
				continue;

			case RXRPC_ACK:
				nondata = 1;
				printf("RXRPC_ACK");
				if (n != 0)
					goto dump_data_colon;
				goto print_nl;

			case RXRPC_RESPONSE:
				nondata = 1;
				printf("RXRPC_RESPONSE");
				if (n != 0)
					goto dump_data_colon;
				goto print_nl;

			case RXRPC_NET_ERROR:
				nondata = 1;
				printf("RXRPC_NET_ERROR: ");
				if (n != sizeof(abort_code))
					goto dump_data;
				memcpy(&abort_code, p, sizeof(abort_code));
				printf("%s\n", strerror(abort_code));
				continue;

			case RXRPC_BUSY:
				nondata = 1;
				printf("RXRPC_BUSY");
				if (n != 0)
					goto dump_data_colon;
				goto print_nl;

			case RXRPC_LOCAL_ERROR:
				nondata = 1;
				printf("RXRPC_LOCAL_ERROR: ");
				if (n != sizeof(abort_code))
					goto dump_data;
				memcpy(&abort_code, p, sizeof(abort_code));
				printf("%s\n", strerror(abort_code));
				continue;

			case RXRPC_NEW_CALL:
				nondata = 1;
				printf("RXRPC_NEW_CALL\n");
				assign = 1;
				continue;

			default:
				break;
			}
		}

		printf("l=%d t=%d", cmsg->cmsg_level, cmsg->cmsg_type);

	dump_data_colon:
		printf(": ");
	dump_data:
		printf("{");
		for (; n > 0; n--, p++)
			printf("%02x", *p);
		printf("}");

	print_nl:
		printf("\n");
	}

	if (assign)
		assign_call_ID();
	if (!nondata && !(msg->msg_flags & MSG_MORE))
		send_reply(user_id);
}

void dump_data(void *p, size_t len)
{
#define INT_PER_LINE (8 * 4)
	size_t loop;
	unsigned char buf[41], *b, ch, yoffs;

	yoffs = 0;
	b = buf;
	for (loop = 0; loop < len; loop++) {
		if (!yoffs) {
			printf("%08zx: ", loop);
			yoffs = 1;
		}
		ch = *(const char *) p;
		printf("%02x", ch);
		*b++ = isprint(ch) ? ch : '.';
		p++;
		if (loop % INT_PER_LINE == INT_PER_LINE - 1) {
			*b = 0;
			printf(" %s\n", buf);
			b = buf;
			yoffs = 0;
		} else if (loop % 4 == 3) {
			putchar(' ');
		}
	}

	if (loop % INT_PER_LINE > 0) {
		for (loop %= INT_PER_LINE; loop < INT_PER_LINE; loop++) {
			printf("  ");
			if (loop % 4 == 3)
				putchar(' ');
		}
		*b = 0;
		printf("%s\n", buf);
	}
}

/*****************************************************************************/
/*
 *
 */
int main(int argc, char *argv[])
{
	struct sockaddr_rxrpc srx;
	unsigned char buffer[4096], control[4096];
	struct msghdr msg;
	struct iovec iov[3];
	int ret, loop, keyring, key;

	/* make the server fall over randomly occasionally */
	signal(SIGALRM, SIG_DFL);
	//alarm(5);

	server = socket(AF_RXRPC, SOCK_DGRAM, PF_INET);
	OSERROR(server, "socket");

	keyring = add_key("keyring", "AFSkeys", NULL, 0, KEY_SPEC_PROCESS_KEYRING);
	OSERROR(keyring, "add_key/ring");

	const char secret[8] = { 0xa7, 0x83, 0x8a, 0xcb, 0xc7, 0x83, 0xec, 0x94 };

	key = add_key("rxrpc_s", "52:2", secret, 8, keyring);
	OSERROR(key, "add_key");

	ret = setsockopt(server, SOL_RXRPC, RXRPC_SECURITY_KEYRING,
			 "AFSkeys", 7);
	OSERROR(ret, "set keyring");

	/* bind an address to the local endpoint */
	srx.srx_family = AF_RXRPC;
#ifndef DO_BULK
	srx.srx_service = 52; /* AFS VL service */
#else
	srx.srx_service = 4; /* BULK service */
#endif
	srx.transport_type = SOCK_DGRAM;
	srx.transport_len = sizeof(srx.transport.sin);
	srx.transport.sin.sin_family = AF_INET;
#ifndef DO_BULK
	srx.transport.sin.sin_port = htons(7003); /* VL server */
#else
	srx.transport.sin.sin_port = htons(34835); /* BULK server */
#endif
	memcpy(&srx.transport.sin.sin_addr, &local_addr, 4);

	ret = bind(server, (struct sockaddr *) &srx, sizeof(srx));
	OSERROR(ret, "bind");

	//system("netstat -ua");

	/* wait for calls to come in */
	ret = listen(server, 4);
	OSERROR(ret, "listen");

	/* wait for calls to come in */
	for (;;) {
		iov[0].iov_base = buffer;
		iov[0].iov_len = sizeof(buffer);

		msg.msg_name = NULL;
		msg.msg_namelen = 0;
		msg.msg_iov = iov;
		msg.msg_iovlen = 1;
		msg.msg_control = control;
		msg.msg_controllen = sizeof(control);
		msg.msg_flags = 0;

		ret = recvmsg(server, &msg, 0);
		OSERROR(ret, "recvmsg");

		printf("RECV: %d [fl:%d]\n", ret, msg.msg_flags);
		printf("CMSG: %zu\n", msg.msg_controllen);
		printf("IOV: %zu [0]=%zu\n", msg.msg_iovlen, iov[0].iov_len);
		dump_cmsg(&msg);

#if 1
		for (loop = 0; loop < ret; loop++) {
			printf("%02x", buffer[loop]);
			if (loop % 40 == 39)
				putchar('\n');
			else if (loop % 4 == 3)
				putchar(' ');
		}
		putchar('\n');
#endif
	}
}
